package org.fujay.langchain4jserver.application.listener;

import cn.hutool.core.collection.ListUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fujay.langchain4jserver.domain.knowledge.aggregate.KnowledgeBaseAggregate;
import org.fujay.langchain4jserver.domain.knowledge.entity.File;
import org.fujay.langchain4jserver.domain.knowledge.event.FileAddedEvent;
import org.fujay.langchain4jserver.domain.knowledge.event.FileIndexingFailed;
import org.fujay.langchain4jserver.domain.knowledge.event.FileRemovedEvent;
import org.fujay.langchain4jserver.domain.knowledge.event.KnowledgeBaseDeletedEvent;
import org.fujay.langchain4jserver.domain.knowledge.exception.KnowledgeBaseNotFoundException;
import org.fujay.langchain4jserver.domain.knowledge.exception.KnowledgeFileNotFoundException;
import org.fujay.langchain4jserver.domain.knowledge.repository.FileRepository;
import org.fujay.langchain4jserver.domain.knowledge.repository.KnowledgeBaseRepository;
import org.fujay.langchain4jserver.domain.knowledge.service.IndexingService;
import org.fujay.langchain4jserver.domain.knowledge.vo.FileId;
import org.fujay.langchain4jserver.domain.knowledge.vo.IndexStatus;
import org.fujay.langchain4jserver.domain.shared.service.StorageProvider;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author slm
 * @description 知识库文件变更事件监听器
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class KnowledgeEventListener {

    private final IndexingService indexingService;
    private final KnowledgeBaseRepository knowledgeBaseRepository;
    private final FileRepository fileRepository;
    private final StorageProvider storageProvider;

    /**
     * 处理知识库删除事件（异步清理）
     *
     * @param event 事件对象
     */
    @Async
    @EventListener
    public void handleKnowledgeBaseDeleted(KnowledgeBaseDeletedEvent event) {
        log.info(
                "接收到知识库删除事件，开始异步清理: kbId={}, indexName={}",
                event.getKnowledgeBaseId().value(),
                event.getIndexName());

        // 1. 清理物理文件
        try {
            // 需要查询该知识库下的所有文件信息
            List<File> filesToDelete = fileRepository.findByKnowledgeBaseId(event.getKnowledgeBaseId());
            log.info(
                    "找到 {} 个待删除的物理文件，隶属于知识库 {}", filesToDelete.size(), event.getKnowledgeBaseId().value());
            for (File file : filesToDelete) {
                try {
                    storageProvider.deleteFile(file);
                    log.info("已异步删除物理文件: {}", file.getFilePath());
                } catch (Exception e) {
                    log.error(
                            "异步删除物理文件 {} 失败 (kbId={}): {}",
                            file.getFilePath(),
                            event.getKnowledgeBaseId().value(),
                            e.getMessage(),
                            e);
                }
            }
            log.info("知识库 {} 的物理文件清理完成。", event.getKnowledgeBaseId().value());
        } catch (Exception e) {
            log.error(
                    "查询知识库 {} 的文件列表以进行物理删除时出错: {}", event.getKnowledgeBaseId().value(), e.getMessage(), e);
        }

        // 2. 清理向量存储索引
        if (event.getIndexName() != null && !event.getIndexName().isBlank()) {
            try {
                indexingService.deleteKnowledgeBaseIndex(event.getKnowledgeBaseId());
                log.info("已异步删除知识库 {} 的向量索引: {}", event.getKnowledgeBaseId().value(), event.getIndexName());
            } catch (Exception e) {
                log.error(
                        "异步删除向量索引 {} 失败 (kbId={}): {}",
                        event.getIndexName(),
                        event.getKnowledgeBaseId().value(),
                        e.getMessage(),
                        e);
            }
        } else {
            log.warn("知识库 {} 的索引名称为空，无法清理向量存储。", event.getKnowledgeBaseId().value());
        }

        log.info("知识库 {} 的异步清理任务执行完毕。", event.getKnowledgeBaseId().value());
    }

    @Async
    @EventListener
    public void fileAdd(FileAddedEvent event) {

        FileId fileId = event.getFileId();
        indexingService.indexFile(fileId);
    }

    @Async
    @EventListener
    public void fileRemove(FileRemovedEvent event) {
        indexingService.deleteDocumentSegments(event.getKnowledgeBaseId(),event.getFileId());
    }

    @Async
    @EventListener
    public void fileIndexFail(FileIndexingFailed event) {
        log.error(
                "监听到文件索引失败事件: kbId={}, fileId={}, reason={}",
                event.getKnowledgeBaseId().value(),
                event.getFileId().value(),
                event.getErrorMessage());
        updateFileStatusOnEvent(
                event.getFileId(), ListUtil.empty(), IndexStatus.FAILED, event.getErrorMessage());
    }

    public void updateFileStatusOnEvent(
            FileId fileId, List<String> documentIds, IndexStatus status, String errorMessage) {
        log.debug("通过事件更新文件状态: fileId={}, status={}, error='{}'", fileId.value(), status, errorMessage);
        try {
            File file =
                    fileRepository
                            .findById(fileId)
                            .orElseThrow(() -> new KnowledgeFileNotFoundException(fileId));
            KnowledgeBaseAggregate aggregate =
                    knowledgeBaseRepository
                            .findById(file.getKnowledgeBaseId())
                            .orElseThrow(
                                    () -> new KnowledgeBaseNotFoundException(file.getKnowledgeBaseId(), "知识库不存在"));

            aggregate.updateFileIndexStatus(fileId, documentIds, status, errorMessage);
            knowledgeBaseRepository.save(aggregate);
            log.info("文件状态更新成功: fileId={}, newStatus={}", fileId.value(), status);
        } catch (Exception e) {
            log.error(
                    "更新文件状态失败: fileId={}, status={}, error: {}", fileId.value(), status, e.getMessage(), e);
        }
    }
}
